package internal

import (
	"fmt"
	"github.com/robfig/cron"
	"go.uber.org/zap"
	"gitee.com/carollia/data-sdk-go/model"
	"gitee.com/carollia/data-sdk-go/utils"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

//var err error

var mu sync.Mutex
var Logger, _ = zap.NewProduction()

// 设置协程数，分发请求
type DisPatcher struct {
	Producer *Producer
	JobQueue chan *model.Request
	cron     *cron.Cron
	CronSpec string
}

func NewDisPatcher(c *model.Config) (d *DisPatcher, err error) {
	if err = c.Normalize(); err != nil {
		return
	}
	Logger, err = utils.NewZapLogger(c.LogLevel)
	defer Logger.Sync()
	if err != nil {
		return
	}
	if c.CronSpec == "" {
		c.CronSpec = "0/10 * * * * ?"
	}
	producer := NewProducer(c.GameId, c.Secret,c.Url, c.Timezone,c.Compression, c.MaxSize, c.Timeout)
	d = &DisPatcher{
		Producer: producer,
		JobQueue: make(chan *model.Request, c.MaxQueue),
		cron:     cron.New(),
		CronSpec: c.CronSpec,
	}
	for i := 0; i < c.MaxConsumer; i++ {
		worker := NewConsumer(d.JobQueue, c.MaxQueue)
		worker.start()
	}
	// 启用定时任务
	if err = d.cron.AddFunc(d.CronSpec, d.timeTask); err != nil {
		return
	}
	d.cron.Start()
	// 监听信号量,重启,关闭等先将数据发送出去
	d.Flush()
	return
}

// 分发任务
func (d *DisPatcher) Dispatch(data model.EventData) (err error) {
	request, err := d.Producer.send(data)
	if err != nil {
		return
	}
	// 将请求任务分发到队列中
	if request != nil {
		d.JobQueue <- request
	}
	return
}

func (d *DisPatcher) timeTask() {
	request := d.Producer.getRequest(1)
	if request != nil {
		d.JobQueue <- request
	}
}

func (d *DisPatcher) Flush()  {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		for {
			s := <-c
			fmt.Println("get a signal " + s.String())
			switch s {
			case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
				request := d.Producer.getRequest(2)
				if request != nil {
					d.JobQueue <- request
				}
				time.Sleep(time.Second)
				if len(d.JobQueue) <= 0 {
					os.Exit(0)
				} else {
					fmt.Println("Sleep 2 s for send data")
					time.Sleep(time.Second * 2)
					os.Exit(0)
				}
				return
			case syscall.SIGHUP:
			default:
				return
			}
		}
	}()
}
